﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms.DataVisualization.Charting;
using Microsoft.SqlServer.XEvent.Linq;


namespace SQLWaitsProfiler
{
    public partial class Form1 : Form
    {
        BackgroundWorker bw = new BackgroundWorker();
        Chart chartMain;
        DataTable table1;
        DateTime startTime = DateTime.MinValue;

        public Form1()
        {
            InitializeComponent();
            bw.WorkerSupportsCancellation = true;
            bw.WorkerReportsProgress = true;
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
        }

        void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {/*
            DataPoint Dp = new DataPoint();
            Dp.SetValueY(e.ProgressPercentage);
            chart1.Series[0].Points.Add(Dp);
            */
            /*
            DataRow dr = table1.NewRow();
            dr["wait_type"] = "TEST" + Guid.NewGuid().ToString();
            dr["total_duration"] = e.ProgressPercentage;
            table1.Rows.Add(dr);
            */

            chartMain.DataBind();
        }

        int pos_CXPACKET = -1;
        private void AddChartPoint(DataPoint dp, string seriesName)
        {
            Series series = null;
            foreach(Series s in chartMain.Series)
            {
                if (s.Name == seriesName)
                {
                    series = s;
                    break;
                }
            }
            if (series == null)
            {
                series = new Series(seriesName);
                series["DrawSideBySide"] = "False";
                series["IsXAxisQuantitative"] = "True";
                series.BorderWidth = 5;
                series.ChartArea = "ChartArea";
                series.ChartType = SeriesChartType.RangeBar;
                //series.Legend = "Legend1";
                //series.ToolTip = string.Format("Wait type: {0}", series.Name);// "#AXISLABEL (#VAL)";
                
                if (seriesName == "CXPACKET")
                {
                    series["PointWidth"] = "0.5";
                    chartMain.Series.Add(series);
                    series.Color = Color.FromArgb(140, 100, 255, 100); //transparent green ?!
                    pos_CXPACKET = chartMain.Series.Count - 1;
                }
                else
                {
                    if (pos_CXPACKET == -1)
                        chartMain.Series.Add(series);
                    else
                        chartMain.Series.Insert(chartMain.Series.Count - 1, series);
                }
                
  
            }
            /*
            if (dp.YValues[1] > chartMain.ChartAreas[0].AxisY.Minimum + 15000)
            {
                chartMain.ChartAreas[0].AxisY.Minimum += 5000;
                chartMain.ChartAreas[0].AxisY.Maximum = chartMain.ChartAreas[0].AxisY.Minimum + 15000;
            }
             * */
            dp.ToolTip = string.Format("Wait type: {0}\r\nDuration: {1} ms", series.Name, (dp.YValues[1] - dp.YValues[0]));
            series.Points.Add(dp);

            if (chartMain.ChartAreas[0].AxisY.Maximum > chartMain.ChartAreas[0].AxisY.ScaleView.Size)
                chartMain.ChartAreas[0].AxisY.ScaleView.Scroll(ScrollType.Last);

            //chartMain.ChartAreas[0].AxisY.ScaleView.Scroll(ScrollType.Last);
            //chartMain.AutoScrollOffset = ;
        }
     
        private void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            // .\SQL2012
            // ws136-ak
            // us-oma-db-bs201\dev2
            QueryableXEventData events = new QueryableXEventData(
                            @"Data Source = .\SQL2012; Initial Catalog = master; Integrated Security = SSPI",
                            "MonitorWaits",
                            //"tempdb_io",
                            EventStreamSourceOptions.EventStream,
                            EventStreamCacheOptions.DoNotCache);

            int i = 0;
            foreach (PublishedEvent evt in events)
            {
                if ((worker.CancellationPending == true))
                {
                    //chartMain.Dispose();
                    e.Cancel = true;
                    break;
                }
             
                i++;
                if (i > this.Width)
                {
                    i = 1;
                }

                int cpu_id = Convert.ToInt32(evt.Actions["cpu_id"].Value);
                int session_id = Convert.ToInt32(evt.Actions["session_id"].Value);
                DateTime timestamp = evt.Timestamp.DateTime;
                int duration = Convert.ToInt32(evt.Fields["duration"].Value);
                string wait_type = Convert.ToString(evt.Fields["wait_type"].Value);
                /*
                if (evt.Name == "file_read_completed")
                {
                    wait_type = "file_read_completed";
                }

                if (evt.Name == "file_write_completed")
                {
                    wait_type = "file_write_completed";
                }
                */
                //DataTable table1 = dataTable1;
                
                if (table1.Rows.Contains(wait_type))
                {
                    DataRow dr = table1.Rows.Find(wait_type);
                    dr["total_duration"] = (int)dr["total_duration"] + duration;
                }
                else
                {
                    DataRow dr = table1.NewRow();
                    dr["wait_type"] = wait_type;
                    dr["total_duration"] = duration;
                    table1.Rows.Add(dr);
                }

                if (startTime == DateTime.MinValue)
                {
                    startTime = timestamp.AddMilliseconds(-duration);
                }


                double startX = cpu_id; //+ session_id * 4;
                //double endY = timestamp.AddSeconds(1).ToOADate();
                double endY = (timestamp - startTime).TotalMilliseconds;
                //double startY = timestamp.AddMilliseconds(-duration).ToOADate();
                double startY = endY - duration;
                // endY = startY;

                DataPoint dt = new DataPoint(startX, new[] { startY, endY });
                this.BeginInvoke(new Action<DataPoint, string>(AddChartPoint), dt, wait_type);

                //bw.ReportProgress(0);
               

/*
                chart1.DataSource = table1;
                chart1.Series[0].XValueMember = "wait_type";
                chart1.Series[0].YValueMembers = "total_duration";
*/
                /*
                switch (wait_type)
                {
                    case "LATCH_EX":
                    case "LATCH_SH":
                        myPen.Color = Color.Aqua;
                        break;
                    case "PAGEIOLATCH_SH":
                    case "PAGEIOLATCH_UP":
                        myPen.Color = Color.Pink;
                        break;
                    case "CXPACKET":
                        myPen.Color = Color.Orange;
                        break;
                    case "RESOURCE_SEMAPHORE":
                        myPen.Color = Color.GreenYellow;
                        break;
                    case "IO_COMPLETION":
                        myPen.Color = Color.Blue;
                        break;
                    default:
                        myPen.Color = Color.White;
                        break;
                }
                */
/*
                formGraphics.DrawLine(myPen, i, 10, i, 10 + duration);
                myPen.Color = Color.Black;
                formGraphics.DrawLine(myPen, (i + 1) , 10, (i + 1) , 1000);
*/

                //series.Sort(PointSortOrder.Descending, "Y");


                /*
                Console.WriteLine(evt.Name);

                foreach (PublishedEventField fld in evt.Fields)
                {
                    Console.WriteLine("\tField: {0} = {1}", fld.Name, fld.Value);
                }

                foreach (PublishedAction act in evt.Actions)
                {
                    Console.WriteLine("\tAction: {0} = {1}", act.Name, act.Value);
                }
                */
                // Perform a time consuming operation and report progress.


                // if (evt.Fields["field_name"].Value == "foo")

                // You have to explicitly cast the field to a MapValue:
                // if (((MapValue)evt.Fields["field_name"].Value).Value == "foo" )

            }

        }

            /*
                DateTime timestamp = Convert.ToDateTime(dr["start"]);
                int session_id = Convert.ToInt32(dr["session_id"]);
                int cpu_id = Convert.ToInt32(dr["cpu_id"]);
                string wait_type = Convert.ToString(dr["wait_type"]);
                int duration = Convert.ToInt32(dr["duration"]);
                            
                if (startTime == DateTime.MinValue)
                {
                    startTime = timestamp;
                }

                int startX = (int)(Convert.ToDecimal((timestamp - startTime).TotalMilliseconds) / scale);
                int startY = 10 + cpu_id * 10;
                int endX = startX + (int)(Convert.ToDecimal(duration) / scale) + 1;
                int endY = startY;


                            switch (wait_type)
                            {
                                case "LATCH_EX":
                                case "LATCH_SH":
                                    myPen.Color = Color.Aqua;
                                    break;
                                case "PAGEIOLATCH_SH":
                                case "PAGEIOLATCH_UP":
                                    myPen.Color = Color.Pink;
                                    break;
                                case "CXPACKET":
                                    myPen.Color = Color.Orange;
                                    break;
                                case "RESOURCE_SEMAPHORE":
                                    myPen.Color = Color.GreenYellow;
                                    break;
                                case "IO_COMPLETION":
                                    myPen.Color = Color.Blue;
                                    break;
                                default:
                                    myPen.Color = Color.White;
                                    break;
                            }

            */


        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            //e.Graphics.DrawImageUnscaled(this.bb, 0, 0);
            
        }

        private void start_Click(object sender, EventArgs e)
        {

            if (table1 == null)
            {
                table1 = new DataTable();
                table1.Columns.Add("wait_type", typeof(string));
                table1.Columns.Add("total_duration", typeof(int));
                table1.PrimaryKey = new[] { table1.Columns["wait_type"] };
            }

            if (chartMain != null)
            {
                chartMain.Dispose();
            }

            // CHART
            //if (chartMain == null)
            {
                int blockSize = 100;

                chartMain = new Chart();
                ChartArea chartArea = new ChartArea();
                pos_CXPACKET = -1;


//                chartArea.AxisX.MajorGrid.Enabled = false;

                

                //chartArea.AxisX.MajorGrid.Interval = 1;
                chartArea.AxisX.MajorGrid.IntervalOffset = 0.5;
                chartArea.AxisX.MajorGrid.IntervalOffsetType = DateTimeIntervalType.Number;
                chartArea.AxisX.MajorGrid.IntervalType = DateTimeIntervalType.Number;

//                chartArea.AxisX.MinorGrid.Enabled = true;
                /*
                // Instantiate new strip line
                StripLine stripLine1 = new StripLine();
                stripLine1.StripWidth = 0;
                stripLine1.BorderColor = System.Drawing.Color.RoyalBlue;
                stripLine1.BorderWidth = 3;
                stripLine1.Interval = 5;

                // Consider adding transparency so that the strip lines are lighter
                stripLine1.BackColor = System.Drawing.Color.RosyBrown;

                stripLine1.BackSecondaryColor = System.Drawing.Color.Purple;
                stripLine1.BackGradientStyle = GradientStyle.LeftRight;

                // Add the strip line to the chart
                chartArea.AxisY.StripLines.Add(stripLine1);

                StripLine stripLine2 = new StripLine();
                stripLine2.StripWidth = 0;
                stripLine2.BorderColor = System.Drawing.Color.RoyalBlue;
                stripLine2.BorderWidth = 3;
                stripLine2.Interval = 9.5;

                // Consider adding transparency so that the strip lines are lighter
                stripLine2.BackColor = System.Drawing.Color.RosyBrown;

                stripLine2.BackSecondaryColor = System.Drawing.Color.Purple;
                stripLine2.BackGradientStyle = GradientStyle.LeftRight;

                // Add the strip line to the chart
                chartArea.AxisY.StripLines.Add(stripLine2);
                */

                chartArea.Name = "ChartArea";
                chartArea.CursorY.AutoScroll = true;
                chartArea.CursorY.IsUserSelectionEnabled = true;
                //chartArea.CursorY.IntervalType = DateTimeIntervalType.Milliseconds;

                chartArea.AxisX.Title = "CPU";
                chartArea.AxisX.IntervalOffset = 0.5;
                //chartArea.AxisX.LabelStyle.IntervalOffset = 0.5;

                chartArea.AxisX.CustomLabels.Add((double)-0.5, (double)0.5, "cpu:0");
                chartArea.AxisX.CustomLabels.Add((double)0.5, (double)1.5, "cpu:1");
                chartArea.AxisX.CustomLabels.Add((double)1.5, (double)2.5, "cpu:2");
                chartArea.AxisX.CustomLabels.Add((double)2.5, (double)3.5, "cpu:3");


                //chartArea.AxisY.IntervalType = DateTimeIntervalType.Milliseconds;
                //chartArea.AxisY.LabelStyle.Format = "hh:mm:ss:FFFF";
                //chartArea.AxisY.Interval = 10;
                //chartArea.AxisY.LabelStyle.IntervalType = DateTimeIntervalType.Milliseconds;

                chartArea.AxisY.Minimum = 0;
                //chartArea.AxisY.Minimum = DateTime.Now.ToOADate(); ;
                chartArea.AxisY.Title = "Time, ms";
                // chartArea.AxisY.Maximum = blockSize * 100;
                /*
                chartArea.AxisY.Maximum = chartArea.AxisY.Minimum + 15000;
                 * */

                int position = 0;
                //chartArea.AxisX.Minimum = 0;
                chartArea.AxisY.ScaleView.Zoom(position, blockSize * 100);

                // disable zoom-reset button (only scrollbar's arrows are available)
                chartArea.AxisY.ScrollBar.ButtonStyle = ScrollBarButtonStyles.SmallScroll;

                // set scrollbar small change to blockSize (e.g. 100)
                chartArea.AxisY.ScaleView.SmallScrollSize = blockSize;

                chartMain.ChartAreas.Add(chartArea);
                Legend lg = chartMain.Legends.Add("Legend");
                lg.Docking = Docking.Bottom;

                /*
                chartMain.DataSource = table1;
                
                Series series = new Series();

                series.BorderWidth = 5;
                series.ChartArea = "ChartArea";
                series.ChartType = SeriesChartType.Column;
                series.Legend = "Legend1";
                series.Name = "RowCount";

                series.XValueMember = "wait_type";
                series.YValueMembers = "total_duration";

                series.ToolTip = "#AXISLABEL (#VAL)";

                
                chartMain.Series.Add(series);
                 * */
                chartMain.Dock = DockStyle.Fill;
                panel1.Controls.Add(chartMain);
                 
            }
            /*
            DataRow dr = table1.NewRow();
            dr["wait_type"] = "TEST0";
            dr["total_duration"] = 1;
            table1.Rows.Add(dr);
            */

            if (bw.IsBusy != true)
            {



                startTime = DateTime.MinValue;
                bw.RunWorkerAsync();
            }

        }

        private void Cancel_Click(object sender, EventArgs e)
        {
            if (bw.WorkerSupportsCancellation == true)
            {
                bw.CancelAsync();
            }
        }

        private void chart1_Click(object sender, EventArgs e)
        {

        }

        private void button6_Click(object sender, EventArgs e)
        {
            ChartArea chartArea = chartMain.ChartAreas[0];
            AxisScaleView sv = chartArea.AxisY.ScaleView;
            // sv.ViewMaximum

            //chartArea.AxisY.Maximum
            if (double.IsNaN(sv.Position))
            {
                sv.Zoom(0, 0 + sv.ViewMaximum / 2);
            }
            else if (sv.Position == 0)
            {
                sv.Zoom(0, 0 + sv.Size / 2);
            }
            else if (sv.Position + sv.Size == chartArea.AxisY.Maximum)
            {
                double size = (int)sv.Size / 2;
                double viewStart = chartArea.AxisY.Maximum - size;
                sv.Zoom(viewStart, viewStart + size);
            }
            else
            {
                sv.Zoom(sv.Position + (int)(sv.Size*1/4), sv.Position + (int)(sv.Size*3/4));
            }
        }

        private void button7_Click(object sender, EventArgs e)
        {
            ChartArea chartArea = chartMain.ChartAreas[0];
            AxisScaleView sv = chartArea.AxisY.ScaleView;
            if (!double.IsNaN(sv.Position))
            {
                double viewStart = sv.Position - ((int)(sv.Size / 2));
                if (viewStart < 0) 
                    viewStart = 0;
                double size = (int)sv.Size * 2;
                double viewEnd = viewStart + size;
                if (viewEnd > chartArea.AxisY.Maximum)
                    viewEnd = chartArea.AxisY.Maximum;

                sv.Zoom(viewStart, viewEnd);
            }
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            
            foreach (Series series in chartMain.Series)
            {
                series["DrawSideBySide"] = (!checkBox1.Checked ? "False" : "True");
            }
            
        }

        private void button3_Click(object sender, EventArgs e)
        {

        }


    }
}
